CompletableFuture is a class in Java that provides an easy way to write asynchronous and non-blocking code.
Example:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello, World!");
future.thenAccept(System.out::println);
- thenApply(): Transforms the result and returns a new CompletableFuture.
- thenAccept(): Consumes the result without returning a new CompletableFuture.
Example:
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture.allOf(future1, future2).join();
Example using exceptionally():
CompletableFuture.supplyAsync(() -> { throw new RuntimeException("Error!"); })
.exceptionally(ex -> "Recovered from: " + ex.getMessage())
.thenAccept(System.out::println);
handle() allows processing both the result and exception in a single callback.
Example:
CompletableFuture.supplyAsync(() -> "Step 1")
.thenApply(result -> result + " -> Step 2")
.thenApply(result -> result + " -> Step 3")
.thenAccept(System.out::println);
Use runAsync():
CompletableFuture.runAsync(() -> System.out.println("Running asynchronously"));
- get(): Throws checked exceptions.
- join(): Throws unchecked exceptions.
Example using thenCombine():
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
future1.thenCombine(future2, (a, b) -> a + " " + b)
.thenAccept(System.out::println);
- supplyAsync(): Returns a result.
- runAsync(): Does not return a result.
Use thenApply():
CompletableFuture.supplyAsync(() -> "Success")
.thenApply(result -> result + " processed")
.thenAccept(System.out::println);
Use exceptionally():
CompletableFuture.supplyAsync(() -> { throw new RuntimeException("Error!"); })
.exceptionally(ex -> "Recovered from: " + ex.getMessage())
.thenAccept(System.out::println);
Use thenCombine():
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
future1.thenCombine(future2, (a, b) -> a + " " + b)
.thenAccept(System.out::println);
Use applyToEither():
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Fast Task");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Slow Task");
future1.applyToEither(future2, result -> "Result: " + result)
.thenAccept(System.out::println);
Use thenCompose():
CompletableFuture.supplyAsync(() -> "First Task")
.thenCompose(result -> CompletableFuture.supplyAsync(() -> result + " -> Second Task"))
.thenAccept(System.out::println);
Use allOf():
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture.allOf(future1, future2).join();
System.out.println("All tasks completed");
Use anyOf():
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Fast Task");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Slow Task");
CompletableFuture.anyOf(future1, future2)
.thenAccept(result -> System.out.println("First completed: " + result));
Use exceptionally():
CompletableFuture.supplyAsync(() -> { throw new RuntimeException("Error!"); })
.exceptionally(ex -> "Default Value")
.thenAccept(System.out::println);
Use whenComplete():
CompletableFuture.supplyAsync(() -> { throw new RuntimeException("Error!"); })
.whenComplete((result, ex) -> System.out.println("Cleanup action executed"));
Use handle():
CompletableFuture.supplyAsync(() -> { throw new RuntimeException("Failure!"); })
.handle((result, ex) -> result != null ? result : "Recovered from: " + ex.getMessage())
.thenAccept(System.out::println);
Use thenAccept() or peek:
CompletableFuture.supplyAsync(() -> "Task Completed")
.thenAccept(result -> System.out.println("Log: " + result));
Use completeOnTimeout():
CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(5000); } catch (InterruptedException e) { }
return "Finished";
}).completeOnTimeout("Default Value", 3, TimeUnit.SECONDS)
.thenAccept(System.out::println);
Use cancel():
CompletableFuture<String> future = new CompletableFuture<>();
future.cancel(true);
System.out.println("Future cancelled: " + future.isCancelled());
Use completedFuture():
CompletableFuture<String> future = CompletableFuture.completedFuture("Instant Result");
future.thenAccept(System.out::println);
Use failedFuture():
CompletableFuture.failedFuture(new RuntimeException("Something went wrong"))
.exceptionally(ex -> "Handled: " + ex.getMessage())
.thenAccept(System.out::println);
Use supplyAsync():
public CompletableFuture<Integer> asyncMethod() {
return CompletableFuture.supplyAsync(() -> compute());
}
private int compute() { return 42; }
Use runAsync():
CompletableFuture.runAsync(() -> System.out.println("Task running"));
Pass an ExecutorService:
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletableFuture.supplyAsync(() -> "Using custom executor", executor)
.thenAccept(System.out::println);
Use thenCompose():
CompletableFuture.supplyAsync(() -> 10)
.thenCompose(num -> CompletableFuture.supplyAsync(() -> "Result: " + num))
.thenAccept(System.out::println);
Use thenCombine() or thenCombineAsync():
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
future1.thenCombine(future2, (a, b) -> a + " " + b)
.thenAccept(System.out::println);
Use anyOf() and then manually handle timeouts:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture.anyOf(task1, task2).get(2, TimeUnit.SECONDS);
Use exceptionally() or handle():
CompletableFuture.supplyAsync(() -> { throw new RuntimeException("Error!"); })
.exceptionally(ex -> "Error handled")
.thenAccept(System.out::println);
The get() method blocks the thread until the result is available, or an exception is thrown:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
String result = future.get(); // Blocks until result is available
System.out.println(result);
Use thenApply() or thenCompose():
CompletableFuture.supplyAsync(() -> "Task 1")
.thenApply(result -> result + " -> Task 2")
.thenApply(result -> result + " -> Task 3")
.thenAccept(System.out::println);
Use allOf():
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture.allOf(future1, future2).join();
System.out.println("All tasks completed");
thenApply() applies a function to the result, returning a new result.
thenCompose() applies a function returning another CompletableFuture and flattens it:
CompletableFuture.supplyAsync(() -> "Task")
.thenApply(result -> result + " Completed")
.thenCompose(result -> CompletableFuture.supplyAsync(() -> result + " and Finished"))
.thenAccept(System.out::println);
Use sleep() within a supplyAsync task:
CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(3000); } catch (InterruptedException e) { }
return "Delayed Result";
}).thenAccept(System.out::println);
Use runAsync():
CompletableFuture.runAsync(() -> {
System.out.println("Asynchronous Task without result");
});
Use thenApply() to modify the result:
CompletableFuture.supplyAsync(() -> "Initial Result")
.thenApply(result -> result + " -> Transformed")
.thenAccept(System.out::println);
Use thenCompose() to flatten nested CompletableFutures:
CompletableFuture.supplyAsync(() -> "Task 1")
.thenCompose(result -> CompletableFuture.supplyAsync(() -> result + " -> Task 2"))
.thenCompose(result -> CompletableFuture.supplyAsync(() -> result + " -> Task 3"))
.thenAccept(System.out::println);
Use cancel() on the CompletableFuture instance:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(5000); } catch (InterruptedException e) { }
return "Completed";
});
future.cancel(true); // Cancels the task
System.out.println("Cancelled: " + future.isCancelled());
Use allOf() or anyOf() to execute tasks and collect results:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture.allOf(task1, task2).join();
task1.thenAccept(result -> System.out.println("Result: " + result));
task2.thenAccept(result -> System.out.println("Result: " + result));
Use anyOf() to execute an action when any task completes:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture.anyOf(task1, task2)
.thenAccept(result -> System.out.println("Completed: " + result));
Use thenCompose() to chain dependent tasks:
CompletableFuture.supplyAsync(() -> "Task 1")
.thenCompose(result -> CompletableFuture.supplyAsync(() -> result + " -> Task 2"))
.thenCompose(result -> CompletableFuture.supplyAsync(() -> result + " -> Task 3"))
.thenAccept(System.out::println);
Use supplyAsync() to convert:
public CompletableFuture<String> asyncMethod() {
return CompletableFuture.supplyAsync(() -> syncMethod());
}
public String syncMethod() {
return "Synchronous Method Result";
}
Use exceptionally() or handle() to provide a fallback:
CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(3000); } catch (InterruptedException e) { }
return "Result";
})
.completeOnTimeout("Timeout Occurred", 2, TimeUnit.SECONDS)
.thenAccept(System.out::println);
Use thenCombine() to combine results:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
task1.thenCombine(task2, (result1, result2) -> result1 + " " + result2)
.thenAccept(System.out::println);
Use runAsync() to execute a task without expecting a result:
CompletableFuture.runAsync(() -> System.out.println("Task running asynchronously"));
Use anyOf() to get the first completed result:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture.anyOf(task1, task2)
.thenAccept(result -> System.out.println("First completed: " + result));
Use handle() or exceptionally() to handle exceptions and provide a fallback:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
if (true) throw new RuntimeException("Error");
return "Success";
});
future.handle((result, ex) -> {
if (ex != null) {
return "Fallback due to error: " + ex.getMessage();
}
return result;
}).thenAccept(System.out::println);
Use thenCompose() for task chaining:
CompletableFuture.supplyAsync(() -> "Task 1")
.thenCompose(result -> CompletableFuture.supplyAsync(() -> result + " -> Task 2"))
.thenCompose(result -> CompletableFuture.supplyAsync(() -> result + " -> Task 3"))
.thenAccept(System.out::println);
Use thenCombine() to combine two tasks:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
task1.thenCombine(task2, (result1, result2) -> result1 + " " + result2)
.thenAccept(System.out::println);
Use whenComplete() to execute a task after the completion of another:
CompletableFuture.supplyAsync(() -> "Task 1")
.whenComplete((result, ex) -> {
if (ex != null) {
System.out.println("Error occurred: " + ex.getMessage());
} else {
System.out.println("Completed: " + result);
}
})
.thenAccept(System.out::println);
Use supplyAsync() or runAsync() to execute tasks asynchronously:
CompletableFuture.supplyAsync(() -> "Asynchronous Task");
CompletableFuture.runAsync(() -> System.out.println("Running asynchronously"));
Use join() or get() to block the current thread:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Task completed");
String result = future.join(); // Blocks until completion
System.out.println(result);
Use CompletableFuture.allOf() to wait for all tasks:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture.allOf(task1, task2).join();
task1.thenAccept(result -> System.out.println("Result: " + result));
task2.thenAccept(result -> System.out.println("Result: " + result));
Use thenRun() to execute a task after another one completes:
CompletableFuture.supplyAsync(() -> "Task 1")
.thenRun(() -> System.out.println("Task 1 finished, executing Task 2"));
Use completeOnTimeout() to set a timeout for a task:
CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(5000); } catch (InterruptedException e) { }
return "Completed";
})
.completeOnTimeout("Timeout Occurred", 2, TimeUnit.SECONDS)
.thenAccept(System.out::println);
Use anyOf() to get the first completed result:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture.anyOf(task1, task2)
.thenAccept(result -> System.out.println("First completed: " + result));
Use thenCombine() with a set of futures to perform an action once all are completed:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
task1.thenCombine(task2, (result1, result2) -> result1 + " and " + result2)
.thenAccept(System.out::println);
Use thenCombine() when you need to combine two futures of the same type:
CompletableFuture<Integer> task1 = CompletableFuture.supplyAsync(() -> 1);
CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> 2);
task1.thenCombine(task2, Integer::sum)
.thenAccept(result -> System.out.println("Sum: " + result));
Use thenApply() to apply transformations:
CompletableFuture.supplyAsync(() -> "Task 1")
.thenApply(result -> result + " transformed")
.thenApply(result -> result + " again")
.thenAccept(System.out::println);
Use completeOnTimeout() with a custom exception:
CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(5000); } catch (InterruptedException e) { }
return "Task completed";
})
.completeOnTimeout("Timeout Exception", 2, TimeUnit.SECONDS)
.thenAccept(System.out::println);
Use thenRun() to execute after multiple futures:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture.allOf(task1, task2)
.thenRun(() -> System.out.println("All tasks completed"));
Use CompletableFuture.anyOf() to execute on any task completion:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture.anyOf(task1, task2)
.thenAccept(result -> System.out.println("First completed: " + result));
Use completedFuture() to create a future from a value:
CompletableFuture<String> future = CompletableFuture.completedFuture("Already Completed");
future.thenAccept(System.out::println);
Use exceptionally() for exception handling in combined futures:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture.allOf(task1, task2)
.exceptionally(ex -> {
System.out.println("Exception: " + ex.getMessage());
return null;
});
Use multiple supplyAsync() calls to execute tasks concurrently:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
task1.thenAccept(System.out::println);
task2.thenAccept(System.out::println);
Use thenApply() to apply a function:
CompletableFuture.supplyAsync(() -> "Task completed")
.thenApply(result -> result + " and transformed")
.thenAccept(System.out::println);
Use anyOf() to get the first successful result:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Result from Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Result from Task 2");
CompletableFuture.anyOf(task1, task2)
.thenAccept(result -> System.out.println("First completed: " + result));
Use thenCompose() to handle futures sequentially:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
task1.thenCompose(result -> CompletableFuture.supplyAsync(() -> result + " -> Task 2"))
.thenAccept(System.out::println);
Use the Executor parameter in supplyAsync() to run a task in a different thread pool:
Executor executor = Executors.newFixedThreadPool(2);
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> "Task executed in custom thread pool", executor);
task.thenAccept(System.out::println);
Use thenAccept() to perform an action without changing the result:
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> "Task completed");
task.thenAccept(result -> System.out.println("Processed: " + result));
Use exceptionally() to handle exceptions:
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("Something went wrong");
});
task.exceptionally(ex -> "Default value due to exception: " + ex.getMessage())
.thenAccept(System.out::println);
Use get() to block and retrieve the result:
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> "Task result");
String result = task.get();
System.out.println(result);
Use completeOnTimeout() to schedule a delayed execution:
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> "Delayed task");
task.completeOnTimeout("Timeout result", 2, TimeUnit.SECONDS)
.thenAccept(System.out::println);
Use delayedExecutor() to execute a task after a delay:
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> "Delayed task", executor);
task.completeOnTimeout("Completed after timeout", 2, TimeUnit.SECONDS)
.thenAccept(System.out::println);
Use thenCompose() to chain tasks sequentially, ensuring one task starts after the previous one finishes:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "First task");
task1.thenCompose(result -> CompletableFuture.supplyAsync(() -> result + " -> Second task"))
.thenAccept(System.out::println);
Use whenComplete() to run after completion:
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> "Completed task");
task.whenComplete((result, ex) -> {
if (ex != null) {
System.out.println("Error: " + ex.getMessage());
} else {
System.out.println("Result: " + result);
}
});
Use thenCombine() to run two tasks in parallel and combine their results:
CompletableFuture<Integer> task1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> 20);
task1.thenCombine(task2, (result1, result2) -> result1 + result2)
.thenAccept(System.out::println);
Use thenRun() to perform a task after the CompletableFuture completes:
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> "Task completed");
task.thenRun(() -> System.out.println("Post-task action executed"));
Use completedFuture() to create a CompletableFuture with a predefined result:
CompletableFuture<String> task = CompletableFuture.completedFuture("Constant Value");
task.thenAccept(System.out::println);
Use allOf() to wait for all tasks to finish:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture<Void> allTasks = CompletableFuture.allOf(task1, task2);
allTasks.thenRun(() -> System.out.println("All tasks completed"));
Use thenCompose() to run a task asynchronously after another task completes:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1 completed");
task1.thenCompose(result -> CompletableFuture.supplyAsync(() -> result + " -> Task 2"))
.thenAccept(System.out::println);
Use get() with a timeout parameter:
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> "Completed after delay");
String result = task.get(2, TimeUnit.SECONDS);
System.out.println(result);
Use cancel() to cancel a CompletableFuture:
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(5000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
return "Completed";
});
task.cancel(true);
System.out.println("Task canceled: " + task.isCancelled());
Use thenApply() to transform the result of an asynchronous task:
CompletableFuture<Integer> task = CompletableFuture.supplyAsync(() -> 10);
task.thenApply(result -> result * 2)
.thenAccept(System.out::println);
Use handle() to process results or handle exceptions:
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> {
if (true) throw new RuntimeException("Error!");
return "Completed successfully";
});
task.handle((result, ex) -> {
if (ex != null) {
System.out.println("Error: " + ex.getMessage());
} else {
System.out.println("Result: " + result);
}
});
Use a combination of thenApply() and thenAccept() to create a chain of operations:
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> "Task 1");
task.thenApply(result -> result + " -> Task 2")
.thenApply(result -> result + " -> Task 3")
.thenAccept(System.out::println);
Use thenCombine() to handle multiple independent tasks:
CompletableFuture<Integer> task1 = CompletableFuture.supplyAsync(() -> 1);
CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> 2);
task1.thenCombine(task2, (result1, result2) -> result1 + result2)
.thenAccept(System.out::println);
Use thenAccept() to run a callback after a task finishes:
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> "Task Result");
task.thenAccept(result -> System.out.println("Callback executed with: " + result));
Use delay with sleep() inside a CompletableFuture:
CompletableFuture<String> delayedTask = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
return "Delayed Task Result";
});
delayedTask.thenAccept(System.out::println);
Use thenCompose() to chain tasks sequentially:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Start");
task1.thenCompose(result -> CompletableFuture.supplyAsync(() -> result + " -> Next"))
.thenAccept(System.out::println);
Use allOf() to handle multiple independent tasks:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture<Void> allTasks = CompletableFuture.allOf(task1, task2);
allTasks.thenRun(() -> System.out.println("Both tasks are completed"));
Use supplyAsync() to run tasks asynchronously and return a result:
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> "Task completed");
task.thenAccept(result -> System.out.println("Result: " + result));
Use exceptionally() to handle errors in the chain:
CompletableFuture<String> task = CompletableFuture.supplyAsync(() -> {
if (true) throw new RuntimeException("Error occurred");
return "Task Result";
});
task.exceptionally(ex -> "Error handled: " + ex.getMessage())
.thenAccept(System.out::println);
Use thenCombine() for combining futures:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 100);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 200);
future1.thenCombine(future2, (result1, result2) -> result1 + result2)
.thenAccept(System.out::println);
Use runAsync() when you don’t need a result:
CompletableFuture.runAsync(() -> System.out.println("Task completed without a result"));
Use thenApply() to handle successful results:
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
task1.thenCombine(task2, (result1, result2) -> result1 + " & " + result2)
.thenApply(result -> "Combined Result: " + result)
.thenAccept(System.out::println);